//
// Created by yanhai on 2020/2/23.
//

#pragma once

#include <map>
#include <vector>
#include "exception.hpp"

/**
 * 对std::multimap的包装
 * @tparam key_T
 * @tparam value_T
 */
template<typename key_T, typename value_T>
class MultiDict {
    template<typename key_T1, typename value_T1>
    friend std::ostream &operator<<(std::ostream &out, const MultiDict<key_T1, value_T1> &multiDict);

public:
    typedef typename std::multimap<key_T, value_T>::iterator iterator;

    /**
     * 获取一个key对应的值的引用
     * 如果key不存在，则创建一个默认值的key
     * @param key
     * @return 返回key对应value的对象引用
     */
    value_T &operator[](const key_T &key);

    /**
     * 获取一个key对应的值的引用
     * 如果key不存在，则抛出 KeyError 类型异常
     * @param key
     * @return 返回key对应value的对象引用
     */
    const value_T &operator[](const key_T &key) const;

    /**
     * 插入key和对应的value
     * @param key
     * @param value
     * @return 返回指向这个key-value的迭代器
     */
    iterator insert(const key_T &key, const value_T &value);

    /**
     * 获取一个key对应的值
     * 如有多个值只返回其中一个，如果找不到该key的值，则返回默认值
     * @param key
     * @param default_value 默认值，可以不写，值的类型必须有无参构造函数
     * @return 返回值-对象
     */
    value_T get(const key_T &key, const value_T &default_value = value_T()) const;

    /**
     * 获取一个key的所有值
     * @param key
     * @return 使用std::vector容器包含所有值
     */
    std::vector<value_T> getlist(const key_T &key) const;

    /**
     * 判断一个key是否存在
     * @param key
     * @return
     */
    bool exists(const key_T &key) const;

private:
    std::multimap<key_T, value_T> _data;
};

template<typename key_T, typename value_T>
inline value_T &MultiDict<key_T, value_T>::operator[](const key_T &key)
{
    auto it = _data.find(key);
    if (it != _data.end()) {
        return it->second;
    } else {
        it = _data.insert(make_pair(key, key_T()));
        return it->second;
    }
}

template<typename key_T, typename value_T>
const inline value_T &MultiDict<key_T, value_T>::operator[](const key_T &key) const
{
    auto it = _data.find(key);
    if (it != _data.end()) {
        return it->second;
    } else {
        throw KeyError<key_T>(key);
    }
}

template<typename key_T, typename value_T>
inline typename MultiDict<key_T, value_T>::iterator
MultiDict<key_T, value_T>::insert(const key_T &key, const value_T &value)
{
    return _data.insert(make_pair(key, value));
}

template<typename key_T, typename value_T>
inline value_T MultiDict<key_T, value_T>::get(const key_T &key, const value_T &default_value) const
{
    auto it = _data.find(key);
    if (it != _data.end()) {
        return it->second;
    } else {
        return default_value;
    }
}

template<typename key_T, typename value_T>
inline std::vector<value_T> MultiDict<key_T, value_T>::getlist(const key_T &key) const
{
    std::vector<value_T> v;
    auto it = _data.equal_range(key);
    while (it.first != it.second) {
        v.push_back(it.first->second);
        ++it.first;
    }
    return v;
}

template<typename key_T, typename value_T>
bool MultiDict<key_T, value_T>::exists(const key_T &key) const
{
    return _data.find(key) != _data.end();
}

// multimap<string, string>([('aa=', '中文测试'), ('bb', 'abcdf'), ('cc1', 'cc1'), ('cc1', 'cc2'), ('dd', 'dd1')])
template<typename key_T, typename value_T>
inline std::ostream &operator<<(std::ostream &out, const MultiDict<key_T, value_T> &multiDict)
{
    // todo 如何才能打印出 key_T 和 value_T 对应的字符串的值
    //     multimap<key_T, value_T>([
    out << "multimap<string, string>([";
    auto it = multiDict._data.cbegin();
    while (it != multiDict._data.cend()) {
        out << "('" << it->first << "', '" << it->second << "')";
        ++it;
        if (it != multiDict._data.cend()) {
            out << ", ";
        }
    }
    out << "])";
    return out;
}

#include <fstream>

class FileStorage {
public:
    FileStorage(const std::string &stream, const std::string &filename) : _M_stream(stream),
                                                                          _M_filename(filename)
    {
    }

    const std::string &filename() const
    {
        return _M_filename;
    }

    void save(const std::string &path = std::string()) const;

    void save(std::ofstream &dst) const;

private:
    std::string _M_stream;
    std::string _M_filename;
};

void FileStorage::save(const std::string &path) const
{
    std::ofstream fout(!path.empty() ? path : _M_filename);
    if (fout) {
        fout << _M_stream;
        fout.close();
    } else {
        // todo 选择合适的异常类型
        throw RequestException("FileStorage save exception");
    }
}

void FileStorage::save(std::ofstream &dst) const
{
    if (dst) {
        dst << _M_stream;
        dst.close();
    } else {
        // todo 选择合适的异常类型
        throw RequestException("FileStorage save exception");
    }
}

std::ostream &operator<<(std::ostream &os, const FileStorage &fileStorage)
{
    return os << "FileStorage(" << fileStorage.filename() << ")";
}
